﻿/******************************************************************************
* SunnyUI.FrameDecoder 开源TCP、串口数据解码库。
* CopyRight (C) 2022-2023 ShenYongHua(沈永华).
* QQ群：56829229 QQ：17612584 EMail：SunnyUI@qq.com
*
* Blog:   https://www.cnblogs.com/yhuse
* Gitee:  https://gitee.com/yhuse/SunnyUI.FrameDecoder
*
* SunnyUI.FrameDecoder.dll can be used for free under the MIT license.
* If you use this code, please keep this note.
* 如果您使用此代码，请保留此说明。
******************************************************************************
* 文件名称: DecoderHelper.cs
* 文件说明: 数据帧解码器帮助类
* 当前版本: V1.0
* 创建日期: 2022-11-01
*
* 2022-11-01: V1.0.0 增加文件说明
******************************************************************************/

using System;

namespace Sunny.FrameDecoder;

/// <summary>
/// 字节数组解码器复制副本接口
/// </summary>
public interface IByteFrameDecoderCloneable
{
    /// <summary>
    /// 复制一个字节数组解码器
    /// </summary>
    /// <returns>字节数组解码器</returns>
    BaseByteFrameDecoder Clone();
}

internal static class ConstDefine
{
    public const int DefaultNetBufferSize = 1024 * 1024;

    public const int CacheSize = 1024;
}

/// <summary>
/// 数据帧解码器帮助类
/// </summary>
public static class DecoderHelper
{
    /// <summary>
    /// 数据长度类型的长度
    /// </summary>
    /// <param name="valueLengthType">数据长度类型</param>
    /// <returns>长度</returns>
    internal static int Size(this LengthType valueLengthType)
    {
        return (int)valueLengthType;
    }

    internal static int GetLengthValue(this ReadOnlySpan<byte> data, int offset, int length, bool isLittleEndian = true)
    {
        return data.Slice(offset, length).GetLengthValue(isLittleEndian);
    }

    internal static int GetLengthValue(this ReadOnlySpan<byte> span, bool isLittleEndian = true)
    {
        if (span.Length == 1)
        {
            return span[0];
        }

        if (span.Length == 2)
        {
            if (isLittleEndian)
                return (short)(span[0] | span[1] << 8);
            else
                return (short)(span[1] | span[0] << 8);
        }

        if (span.Length == 4)
        {
            if (isLittleEndian)
                return span[0] | span[1] << 8 | span[2] << 16 | span[3] << 24;
            else
                return span[3] | span[2] << 8 | span[1] << 16 | span[0] << 24;
        }

        return -1;
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="buffer"></param>
    /// <param name="startIndex"></param>
    /// <param name="value"></param>
    /// <returns></returns>
    internal static bool TryWriteByte(byte[] buffer, int startIndex, byte value)
    {
        if (unchecked((uint)startIndex) >= unchecked((uint)buffer.Length))
            ThrowHelper.ThrowArgumentOutOfRangeException();
        if (startIndex > buffer.Length - sizeof(byte))
            ThrowHelper.ThrowArgumentException();

        buffer[startIndex] = value;
        return true;
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="buffer"></param>
    /// <param name="startIndex"></param>
    /// <param name="value"></param>
    /// <param name="isLittleEndian"></param>
    /// <returns></returns>
    internal static bool TryWriteUShort(byte[] buffer, int startIndex, ushort value, bool isLittleEndian = true)
    {
        if (unchecked((uint)startIndex) >= unchecked((uint)buffer.Length))
            ThrowHelper.ThrowArgumentOutOfRangeException();
        if (startIndex > buffer.Length - sizeof(short))
            ThrowHelper.ThrowArgumentException();

        Span<byte> bytes = stackalloc byte[sizeof(short)];
        if (isLittleEndian)
        {
            bytes[0] = (byte)value;
            bytes[1] = (byte)(value >> 8);
        }
        else
        {
            bytes[1] = (byte)value;
            bytes[0] = (byte)(value >> 8);
        }

        bytes.CopyTo(buffer.AsSpan().Slice(startIndex, sizeof(ushort)));
        return true;
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="buffer"></param>
    /// <param name="startIndex"></param>
    /// <param name="value"></param>
    /// <param name="isLittleEndian">字节顺序，true为小端字节(低字节序)，false为大端字节序(高字节序)</param>
    /// <returns></returns>
    internal static bool TryWriteUInt(byte[] buffer, int startIndex, uint value, bool isLittleEndian = true)
    {
        if (unchecked((uint)startIndex) >= unchecked((uint)buffer.Length))
            ThrowHelper.ThrowArgumentOutOfRangeException();
        if (startIndex > buffer.Length - sizeof(int))
            ThrowHelper.ThrowArgumentException();

        Span<byte> bytes = stackalloc byte[sizeof(int)];
        if (isLittleEndian)
        {
            bytes[0] = (byte)value;
            bytes[1] = (byte)(value >> 8);
            bytes[2] = (byte)(value >> 16);
            bytes[3] = (byte)(value >> 24);
        }
        else
        {
            bytes[3] = (byte)value;
            bytes[2] = (byte)(value >> 8);
            bytes[1] = (byte)(value >> 16);
            bytes[0] = (byte)(value >> 24);
        }

        bytes.CopyTo(buffer.AsSpan().Slice(startIndex, sizeof(uint)));
        return true;
    }
}

internal static class EmptyArray<T>
{
    public static readonly T[] Value = [];
}